/**
 * @file dragdropimpl.h
 * @brief
 *
 * 
 *
 * @author 1823419256@qq.com
 * @version 1.0
 * @date 2011
*/

#ifndef MYBOOK_CTRLS_DRAGDROPIMPL_H_
#define MYBOOK_CTRLS_DRAGDROPIMPL_H_

#include <shobjidl.h>
#include <shlguid.h>

// CEnumFormatEtc
class CEnumFormatEtc : public IEnumFORMATETC
{
public:
	CEnumFormatEtc(const CSimpleArray<FORMATETC>& ArrFE);
	CEnumFormatEtc(const CSimpleArray<FORMATETC*>& ArrFE);

public:
	// IUnknown
	STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject);
	STDMETHOD_(ULONG, AddRef)();
	STDMETHOD_(ULONG, Release)();

	// IEnumFORMATETC
	STDMETHOD(Next)(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched);
	STDMETHOD(Skip)(ULONG celt);
	STDMETHOD(Reset)();
	STDMETHOD(Clone)(IEnumFORMATETC **ppenum);

private:
	ULONG m_cRef;
	CSimpleArray<FORMATETC> m_pFmtEtc;
	int m_iCur;
};

// CIDropSource
class CIDropSource : public IDropSource
{
public:
	CIDropSource();

public:
	//IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);        
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

	//IDropSource
    STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState);
    STDMETHOD(GiveFeedback)(DWORD dwEffect);

private:
	ULONG m_cRef;
};

// CIDataObject
class CIDataObject : public IDataObject
{
public:
	CIDataObject(CIDropSource* pDropSource);
	~CIDataObject();

public:
	//IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);        
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

	//IDataObject
	STDMETHOD(GetData)(FORMATETC *pformatetcIn, STGMEDIUM *pmedium);
    STDMETHOD(GetDataHere)(FORMATETC *pformatetc, STGMEDIUM *pmedium);
    STDMETHOD(QueryGetData)(FORMATETC *pformatetc);
    STDMETHOD(GetCanonicalFormatEtc)(FORMATETC *pformatectIn, FORMATETC *pformatetcOut);
    STDMETHOD(SetData)(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease);
    STDMETHOD(EnumFormatEtc)(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc);
    STDMETHOD(DAdvise)(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection);
    STDMETHOD(DUnadvise)(DWORD dwConnection);
    STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise);

protected:
	void CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc);

private:
	ULONG m_cRef;
	CSimpleArray<FORMATETC*> m_ArrFormatEtc;
	CSimpleArray<STGMEDIUM*> m_StgMedium;
	CIDropSource* m_pDropSource;
};

// CIDropTarget
class CIDropTarget : public IDropTarget
{
public:
	CIDropTarget(bool bSelfDelete);
	virtual ~CIDropTarget();

public:
	// IUnknown
	STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);
	STDMETHOD_(ULONG, AddRef)(); 
	STDMETHOD_(ULONG, Release)();

	// IDropTarget
	STDMETHOD(DragEnter)(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
    STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
    STDMETHOD(DragLeave)();
    STDMETHOD(Drop)(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);

public:
	//return values: true - release the medium. false - don't release the medium 
	virtual bool OnDrop(FORMATETC* pFmtEtc, STGMEDIUM& medium, DWORD *pdwEffect, HWND hWndFrom, std::vector<int> vecIndices) { return false; }
	virtual bool OnDrop(FORMATETC* pFmtEtc, STGMEDIUM& medium, DWORD *pdwEffect) { return false; }
	virtual bool QueryDrop(DWORD grfKeyState, LPDWORD pdwEffect);

public:
	void SetTargetWnd(HWND hWnd);
	void AddSuportedFormat(FORMATETC& ftetc) { m_arrFormatEtc.Add(ftetc); }

private:
	DWORD m_cRef;
	bool m_bAllowDrop;
	
	CSimpleArray<FORMATETC> m_arrFormatEtc;
	FORMATETC* m_pSupportedFrmt;

	IDropTargetHelper *m_pDropTargetHelper;

	HWND m_hWndTarget;
	bool m_bSelfDelete;

};

class CDragSourceHelper
{
public:
	CDragSourceHelper()
	{
		if(FAILED(CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,
			IID_IDragSourceHelper, (void**)&m_pDragSourceHelper)))
			m_pDragSourceHelper = NULL;
	}
	virtual ~CDragSourceHelper()
	{
		if( m_pDragSourceHelper!= NULL )
		{
			m_pDragSourceHelper->Release();
			m_pDragSourceHelper=NULL;
		}
	}
    
    HRESULT InitializeFromBitmap(HBITMAP hBitmap, POINT& pt, RECT& rc, IDataObject* pDataObject, COLORREF crColorKey = GetSysColor(COLOR_WINDOW))
	{
		if (m_pDragSourceHelper == NULL)
			return E_FAIL;

		BITMAP bm;
		GetObject(hBitmap, sizeof(bm), &bm);

		SHDRAGIMAGE di;
		di.sizeDragImage.cx = bm.bmWidth;
		di.sizeDragImage.cy = bm.bmHeight;
		di.hbmpDragImage = hBitmap;
		di.crColorKey = crColorKey; 
		di.ptOffset.x = pt.x - rc.left;
		di.ptOffset.y = pt.y - rc.top;

        return m_pDragSourceHelper->InitializeFromBitmap(&di, pDataObject);
	}

    HRESULT InitializeFromBitmap(HBITMAP hBitmap, POINT ptOffset, IDataObject* pDataObject, COLORREF crColorKey)
	{
		if (m_pDragSourceHelper == NULL)
			return E_FAIL;

        BITMAP bm;
        GetObject(hBitmap, sizeof(bm), &bm);

		SHDRAGIMAGE di;
        di.sizeDragImage.cx = bm.bmWidth;
        di.sizeDragImage.cy = bm.bmHeight;
        di.hbmpDragImage = hBitmap;
        di.crColorKey = crColorKey; 
        di.ptOffset.x = ptOffset.x;
        di.ptOffset.y = ptOffset.y;

        return m_pDragSourceHelper->InitializeFromBitmap(&di, pDataObject);
	}

    HRESULT InitializeFromWindow(HWND hwnd, POINT& pt,IDataObject* pDataObject)
	{		
		if(m_pDragSourceHelper == NULL)
			return E_FAIL;

		return m_pDragSourceHelper->InitializeFromWindow(hwnd, &pt, pDataObject);
	}

	IDragSourceHelper* GetIDragSourceHelper()
	{
		return m_pDragSourceHelper;
	}

protected:
	IDragSourceHelper* m_pDragSourceHelper;
};

#endif // MYBOOK_CTRLS_DRAGDROPIMPL_H_